#pragma once
#include "bundle.h"
#include "./log/log.h"
#include <time.h>
#include <string>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <jsoncpp/json/json.h>
#include <memory>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
namespace cloud
{
    class Fileutil
    {
    public:
        Fileutil(const std::string filename)
            : filename_(filename)
        {
            ;
        }
        void Remove()
        {
            remove(filename_.c_str());
        }
        // 获取文件属性,包括大小,最近访问时间,最近修改时间
        int fileSize()
        {
            struct stat buf;
            if (stat(filename_.c_str(), &buf) < 0)
            {
                FATAL("stat err 错误码: %d 错误消息: %s", errno, strerror(errno));
                return 0;
            }
            return buf.st_size;
        }
        int fileModtime()
        {
            struct stat buf;
            if (stat(filename_.c_str(), &buf) < 0)
            {
                FATAL("stat err 错误码: %d 错误消息: %s", errno, strerror(errno));
                return 0;
            }
            return buf.st_mtime;
        }
        int fileAcctime()
        {
            struct stat buf;
            if (stat(filename_.c_str(), &buf) < 0)
            {
                FATAL("stat err 错误码: %d 错误消息: %s", errno, strerror(errno));
                return 0;
            }
            return buf.st_mtime;
        }
        std::string Filename()
        {
            //./a/b/c
            int pos = filename_.find_last_of("/");
            // test.txt
            return filename_.substr(pos + 1);
        }
        bool GetPosLen(int pos, int len, std::string *body)
        {
            if (pos + len > fileSize()) // 以免读取越界
            {
                INFO("Get err:over size 错误码: %d 错误消息: %s",errno,strerror(errno));
                return false;
            }
            std::ifstream ifs;
            ifs.open(filename_.c_str(), std::ios::binary);
            if (ifs.is_open() == false)
            {
                FATAL("Get open err:  错误码: %d 错误消息: %s",errno,strerror(errno));
                ifs.close();
                return false;
            }

            ifs.seekg(pos, std::ios::beg);
            // 获取文件数据
            body->resize(len);
            ifs.read(&(*body)[0], len);
            ifs.close();
            return true;
        }
        bool GetContent(std::string *body)
        {
            return GetPosLen(0, fileSize(), body);
        }
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            // 打开文件将数据写入
            ofs.open(filename_, std::ios::binary); // 会创建文件
            if (ofs.is_open() == false)
            {
                FATAL("set content open err: 错误码: %d 错误消息: %s",errno,strerror(errno));
                ofs.close();
                return false;
            }
            ofs.write(&body[0], body.size());
            ofs.close();
            return true;
        }
        // 压缩和解压接口
        bool Compress(const std::string packname) // 传入压缩文件名
        {
            // 1 获取文件数据
            std::string body;
            if (!GetContent(&body))
            {
                FATAL("compress get content false 错误码: %d 错误消息: %s",errno,strerror(errno));
                return false;
            }
            // 2 压缩
            std::string packed = bundle::pack(bundle::LZIP, body);
            // 3 打开文件将压缩数据写入
            Fileutil fu(packname);
            if (fu.SetContent(packed) == false)
            {
                FATAL("compress set content false 错误码: %d 错误消息: %s",errno,strerror(errno));
                return false;
            }
            return true;
        }
        bool UnCompress(const std::string unpackname) // 传入解压文件名
        {
            // 1 获取文件数据
            std::string body;
            if (!GetContent(&body))
            {
                FATAL("UnCompress Get content fail",errno,strerror(errno));
                return false;
            }
            // 2 解压
            std::ofstream ofs;
            std::string unpacked = bundle::unpack(body);

            // 3 打开文件将解压数据写入
            Fileutil fu(unpackname);
            if (fu.SetContent(unpacked) == false)
            {
                FATAL("UnCompress set content fail",errno,strerror(errno));
                return false;
            }
            return true;
        }
        bool Exits()
        {
            return fs::exists(filename_);
        }
        bool creat_director()
        {
            if (Exits())
            {
                return true;
            }
            return fs::create_directories(filename_);
        }
        bool ScanDirectory(std::vector<std::string> *vs)
        {
            for (auto p : fs::directory_iterator(filename_))
            {
                if (fs::is_directory(p))
                    continue;
                vs->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }

    private:
        std::string filename_;
    };
    class Jsonutil
    {
    public:
        static bool serialize(std::string *str, const Json::Value &root)
        {
            Json::StreamWriterBuilder Swb;
            std::shared_ptr<Json::StreamWriter> sw(Swb.newStreamWriter());
            std::stringstream ss;
            sw->write(root, &ss);
            *str = ss.str();
            return true;
        }
        static bool Unserialize(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::shared_ptr<Json::CharReader> cr(crb.newCharReader());
            std::stringstream ss;
            std::string err;
            cr->parse(&str[0], &str[0] + str.size(), root, &err);
            return true;
        }
    };

};